<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:pkg="http://expath.org/ns/pkg"
                xmlns:doc="http://www.oxygenxml.com/ns/doc/xsl"
                xmlns:gdata="http://fgeorges.org/ns/google/data"
                xmlns:atom="http://www.w3.org/2005/Atom"
                xmlns:os="http://a9.com/-/spec/opensearch/1.1/"
                xmlns:http="http://expath.org/ns/http-client"
                xmlns:impl="urn:X-FGeorges:xslt:google:data:impl"
                xmlns:x2x="TODO-xslt-to-xquery"
                x2x:target-ns="http://fgeorges.org/ns/google/data"
                exclude-result-prefixes="#all"
                version="2.0">
           <!-- x2x:local-ns="urn:X-FGeorges:xslt:google:data:impl" -->

   <xsl:import href="http://expath.org/ns/http-client.xsl"
               x2x:target-ns="http://expath.org/ns/http-client"/>
   <!-- TODO: Relative imports do not work with eXist and its
        "resource:" scheme.  So I have to expose the implementation
        module in the config...  This is a bug IMHO.  See my post on
        http://markmail.org/thread/jltekeibzhpynrbv. -->
   <!--xsl:import href="gdata-impl.xsl" x2x:at="gdata-impl.xq"
               x2x:target-ns="urn:X-FGeorges:xslt:google:data:impl"/-->
   <xsl:import href="gdata-impl.xsl"
               x2x:target-ns="urn:X-FGeorges:xslt:google:data:impl"/>

   <pkg:import-uri>http://fgeorges.org/ns/google/gdata.xsl</pkg:import-uri>


   <!-- ...........................................................................................
      Table of contents.
      
      - global variables
      - HTTP handling (TODO: to remove)
      - authentication and service factories
      - query compilation and execution
      - query parameters
      - get feeds and entries
      - create new entries (TODO: to be done)
      - error handling
   -->

   <doc:doc scope="stylesheet">
      <doc:desc>
         <doc:p><doc:b>Created on:</doc:b> Feb 1, 2009</doc:p>
         <doc:p><doc:b>Author:</doc:b> Florent Georges</doc:p>
         <doc:p>XSLT module providing Google Data API 2.0.</doc:p>
         <doc:p>
            Copyright (c) statement at the end of the file.
         </doc:p>
         <doc:p>
            See Google Data API documentation at
            <doc:a href="http://code.google.com/apis/gdata/"/>.
         </doc:p>
      </doc:desc>
   </doc:doc>


   <!-- ...........................................................................................
      Global variables.
      
      - $gdata:default-max-results-by-chunk
   -->

   <doc:doc>
      <doc:desc>
         When retrieving the whole content of a feed, we actually
         retrieve it several chunks if it is too big.  The user can
         provide the maximum number of entries to get in one chunk when
         calling the function.  If he/she does not, this parameter sets
         the default value to use.
      </doc:desc>
   </doc:doc>
   <xsl:param name="gdata:default-max-results-by-chunk" as="xs:integer" select="25"/>


   <!-- ...........................................................................................
      HTTP handling.
      
      - gdata:encore-params($uri-path, $params)
      
      Are in gdata: instead of impl:, to enable satelites stylesheets
      (and maybe users) to use it instead of referencing directly the
      HTTP Client Java namespace.  Though this is a semi-public (or
      semi-private) function.
   -->

   <!--
      TODO: To remove.  Might be still used somewhere in g*.xsl...
   -->
   <doc:doc>
      <doc:desc>Add the query part to a URI path, based on params.</doc:desc>
      <doc:param name="uri-path">
         The URI path (a URI without query part.)
      </doc:param>
      <doc:param name="params">
         The parameters to encode in the URI query part.  Each parameter is an element of
         the form &lt;gdata:param name="..." value="..."/>.  Categories are treated specially,
         using the GData "/-/&lt;category>" notation.
      </doc:param>
      <doc:return></doc:return>
   </doc:doc>
   <xsl:function name="gdata:encode-params" as="xs:string">
      <!-- the URL path (without query nor fragment parts) -->
      <xsl:param name="uri-path" as="xs:string"/>
      <!-- query parameters -->
      <xsl:param name="params"   as="element(gdata:param)*"/>
      <xsl:variable name="categories" as="element(gdata:param)*" select="
          $params[@name eq 'category']"/>
      <xsl:variable name="other-params" as="element(gdata:param)*" select="
          $params except $categories"/>
      <xsl:sequence select="
          string-join(
            (
              $uri-path,
              if ( exists($categories) ) then
                ('/-', $categories/concat('/', @value))
              else
                (),
              for $p in $other-params
                return (
                  if ( $p is $other-params[1] ) then '?' else '&amp;',
                  (: @name should not need to be encoded, but that could
                     prevent some kind of 'injection' if @name is not
                     correct... :)
                  encode-for-uri($p/@name), '=', encode-for-uri($p/@value)
                )
            ),
            ''
          )
        "/>
   </xsl:function>


   <!-- ...........................................................................................
      Authentication and service factories.
      
      - gdata:authenticate-login($service, $email, $pwd)
      - gdata:auth-sub-session-token($token)
      - gdata:make-login-service($service, $token)
      - gdata:make-login-service($service, $token, $version)
      - gdata:make-authsub-service($service, $token)
      - gdata:make-authsub-service($service, $token, $version)
   -->

   <doc:doc>
      <doc:desc>Authenticate the user using its username and password
         (using the Client Login authentication mechanism).</doc:desc>
      <doc:param name="service">
         The service name to authenticate to, as defined by each Google service.
      </doc:param>
      <doc:param name="email">
         The email address of the user to authenticate.
      </doc:param>
      <doc:param name="pwd">
         The password of the user.
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:authenticate-login" as="xs:string">
      <!-- the service name -->
      <xsl:param name="service" as="xs:string"/>
      <!-- the email (user account) -->
      <xsl:param name="email"   as="xs:string"/>
      <!-- the password -->
      <xsl:param name="pwd"     as="xs:string"/>
      <!-- '@gmail.com' at the end of $email is optional -->
      <xsl:variable name="full-email" select="
          if ( contains($email, '@') ) then
            $email
          else
            concat($email, '@gmail.com')"/>
      <!-- the authentication HTTP request -->
      <xsl:variable name="request" as="element(http:request)">
         <http:request method="post" href="{ $impl:auth-login-endpoint }">
            <!-- regardless of the GData version for the service itself, use 2 here -->
            <http:header name="GData-Version" value="2.0"/>
            <http:body media-type="application/x-www-form-urlencoded">
               <xsl:text>Email=</xsl:text>
               <xsl:value-of select="encode-for-uri($full-email)"/>
               <xsl:text>&amp;Passwd=</xsl:text>
               <xsl:value-of select="encode-for-uri($pwd)"/>
               <xsl:text>&amp;source=</xsl:text>
               <xsl:value-of select="encode-for-uri($impl:app-name)"/>
               <xsl:text>&amp;service=</xsl:text>
               <xsl:value-of select="encode-for-uri($service)"/>
               <xsl:text>&amp;accountType=</xsl:text>
               <xsl:value-of select="
                   if ( ends-with($full-email, '@gmail.com') ) then
                     'GOOGLE'
                   else
                     'HOSTED_OR_GOOGLE'"/>
            </http:body>
         </http:request>
      </xsl:variable>
      <!-- ...send the request and get the response -->
      <xsl:variable name="resp" select="http:send-request($request)"/>
      <!-- ...does the response look ok? -->
      <xsl:if test="count($resp) ne 2">
         <xsl:sequence select="gdata:error('GDATA001')"/>
      </xsl:if>
      <xsl:if test="not($resp[2] instance of xs:string)">
         <xsl:sequence select="gdata:error('GDATA002', $resp[1]/http:body/@media-type)"/>
      </xsl:if>
      <xsl:if test="xs:integer($resp[1]/@status) ne 200">
         <xsl:sequence select="gdata:error('GDATA003', $resp[1]/@status, $resp[1]/@message)"/>
      </xsl:if>
      <!-- ...get the auth token within the response -->
      <xsl:sequence select="
          substring-after(
            tokenize($resp[2], '&#10;')[starts-with(., 'Auth=')],
            '=')"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>Ask for a session token from an authentication token
         got from the Auth Sub mechanism.</doc:desc>
      <doc:param name="token">
         The authentication token got from Auth Sub.
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:auth-sub-session-token" as="xs:string">
      <!-- the token -->
      <xsl:param name="token" as="xs:string"/>
      <!-- the authentication HTTP request -->
      <xsl:variable name="request" as="element(http:request)">
         <http:request method="get" href="{ $impl:auth-sub-session-endpoint }">
            <!-- regardless of the GData version for the service itself, use 2 here -->
            <http:header name="GData-Version" value="2.0"/>
            <!-- the authorization header -->
            <http:header name="Authorization" value='AuthSub token="{ $token }"'/>
         </http:request>
      </xsl:variable>
      <!-- ...send the request and get the response -->
      <xsl:variable name="resp" select="http:send-request($request)"/>
      <!-- ...does the response look ok? -->
      <xsl:if test="count($resp) ne 2">
         <xsl:sequence select="gdata:error('GDATA001')"/>
      </xsl:if>
      <xsl:if test="not($resp[2] instance of xs:string)">
         <xsl:sequence select="gdata:error('GDATA002', $resp[1]/http:body/@media-type)"/>
      </xsl:if>
      <xsl:if test="xs:integer($resp[1]/@status) ne 200">
         <xsl:sequence select="gdata:error('GDATA003', $resp[1]/@status, $resp[1]/@message)"/>
      </xsl:if>
      <!-- ...get the auth token within the response -->
      <xsl:sequence select="
          substring-after(
            tokenize($resp[2], '&#10;')[starts-with(., 'Token=')],
            '=')"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>Make a new service object, with Client Login authentication.</doc:desc>
      <doc:param name="service">
         The service name, as defined by each Google service.
      </doc:param>
      <doc:param name="token">
         The authentication token.
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:make-login-service" as="element(gdata:service)">
      <!-- the service name -->
      <xsl:param name="service" as="xs:string"/>
      <!-- the auth token -->
      <xsl:param name="token"   as="xs:string"/>
      <!-- by default, use the GData protocol version 2 -->
      <xsl:sequence select="gdata:make-login-service($service, $token, '2.0')"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>Make a new service object, with Client Login authentication.</doc:desc>
      <doc:param name="service">
         The service name, as defined by each Google service.
      </doc:param>
      <doc:param name="token">
         The authentication token.
      </doc:param>
      <doc:param name="version">
         The version of the GData protocol to use.
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:make-login-service" as="element(gdata:service)">
      <!-- the service name -->
      <xsl:param name="service" as="xs:string"/>
      <!-- the auth token -->
      <xsl:param name="token"   as="xs:string"/>
      <!-- the GData version -->
      <xsl:param name="version" as="xs:string"/>
      <!-- the service element to return -->
      <gdata:service name="{ $service }" gdata-version="{ $version }">
         <gdata:client-login>
            <xsl:value-of select="$token"/>
         </gdata:client-login>
      </gdata:service>
   </xsl:function>

   <doc:doc>
      <doc:desc>Make a new service object, with AuthSub authentication.</doc:desc>
      <doc:param name="service">
         The service name, as defined by each Google service.
      </doc:param>
      <doc:param name="token">
         The authentication token.
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:make-authsub-service" as="element(gdata:service)">
      <!-- the service name -->
      <xsl:param name="service" as="xs:string"/>
      <!-- the auth token -->
      <xsl:param name="token"   as="xs:string"/>
      <!-- by default, use the GData protocol version 2 -->
      <xsl:sequence select="gdata:make-authsub-service($service, $token, '2.0')"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>Make a new service object, with AuthSub authentication.</doc:desc>
      <doc:param name="service">
         The service name, as defined by each Google service.
      </doc:param>
      <doc:param name="token">
         The authentication token.
      </doc:param>
      <doc:param name="version">
         The version of the GData protocol to use.
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:make-authsub-service" as="element(gdata:service)">
      <!-- the service name -->
      <xsl:param name="service" as="xs:string"/>
      <!-- the auth token -->
      <xsl:param name="token"   as="xs:string"/>
      <!-- the GData version -->
      <xsl:param name="version" as="xs:string"/>
      <!-- the service element to return -->
      <gdata:service name="{ $service }" gdata-version="{ $version }">
         <gdata:auth-sub>
            <xsl:value-of select="$token"/>
         </gdata:auth-sub>
      </gdata:service>
   </xsl:function>


   <!-- ...........................................................................................
      Query compilation and execution.
      
      - gdata:query($service, $query)
      - gdata:query($service, $query, $since)
      - gdata:compile-query($query)
      - gdata:exec-query($service, $query)
      - gdata:exec-query($service, $query, $since)
      - gdata:query-all($service, $query, $start)
      
      TODO: Document the whole thing.
   -->

   <doc:doc>
      <doc:desc>TODO: Bla bla...</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="query">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:query" as="item()?">
      <!-- the service element -->
      <xsl:param name="service" as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="query"   as="element()+"/>
      <!-- compile and execute the query -->
      <xsl:sequence select="gdata:query($service, $query, ())"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>TODO: Bla bla...</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="query">TODO: Bla bla...</doc:param>
      <doc:param name="since">
         TODO: Bla bla...  TODO: See how I can generalize this
         (for instance by introduce them as paramaters, as new
         possible elements in $query).
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:query" as="item()?">
      <!-- the service element -->
      <xsl:param name="service" as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="query"   as="element()+"/>
      <!-- xs:date, xs:dateTime or xs:string (an ETag) -->
      <xsl:param name="since"   as="item()?"/>
      <!-- compile and execute the query -->
      <xsl:sequence select="
          gdata:exec-query(
            $service,
            gdata:compile-query($query),
            $since)"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>
         <doc:p>Make a new service object, with AuthSub authentication.</doc:p>
         <doc:p>TODO: Detect when we try to compile a compiled query (that is,
            throw an error when $params is an element(gdata:query).  TODO: Adapt
            the parameter serialization to take OR, NOT, etc. into accout...</doc:p>
      </doc:desc>
      <doc:param name="query">
         The raw query, as a set of parameter elements.
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:compile-query" as="element(gdata:query)">
      <xsl:param name="query" as="element()+"/>
<xsl:message>
   GDATA:COMPILE-QUERY: <xsl:copy-of select="$query"/>
</xsl:message>
      <xsl:variable name="feed"   as="element()"  select="$query[local-name(.) eq 'feed']"/>
      <xsl:variable name="cats"   as="element()*" select="$query[local-name(.) eq 'category']"/>
      <xsl:variable name="others" as="element()*" select="$query except ($feed, $cats)"/>
      <xsl:variable name="cat-prefix" as="xs:string" select="
          if ( ends-with($feed, '/' ) ) then '-' else '/-'"/>
      <gdata:query>
         <xsl:sequence select="
             string-join(
               (
                 $feed,
                 if ( exists($cats) ) then
                   ( $cat-prefix, $cats/concat('/', impl:compile-category(.)) )
                 else
                   (),
                 $others/concat(
                   if ( . is $others[1] ) then '?' else '&amp;',
                   encode-for-uri(local-name(.)),
                   '=',
                   encode-for-uri(.))
               ),
               ''
             )
           "/>
      </gdata:query>
   </xsl:function>

   <!-- TODO: Should be in gdata-impl.xsl. -->
   <!-- In XSLT, should use xsl:apply-templates, but use "instance of"
        instead so it can be translated to XQuery. -->
   <xsl:function name="impl:compile-category" as="xs:string">
      <xsl:param name="cat" as="element()"/>
      <xsl:variable name="op" as="xs:string?" select="$cat/@operator"/>
      <xsl:choose>
         <xsl:when test="empty($op)">
            <xsl:sequence select="$cat"/>
         </xsl:when>
         <xsl:when test="$op eq 'not'">
            <xsl:sequence select="concat('-', impl:compile-category($cat/*))"/>
         </xsl:when>
         <xsl:when test="$op eq 'or'">
            <xsl:sequence select="string-join($cat/*/impl:compile-category(.), '%7C')"/>
         </xsl:when>
         <xsl:otherwise>
            <xsl:sequence select="gdata:error('GDATA011', $op)"/>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:function>

   <doc:doc>
      <doc:desc>TODO: Bla bla...</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="query">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:exec-query" as="item()?">
      <!-- the service element -->
      <xsl:param name="service" as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="query"   as="element(gdata:query)"/>
      <!-- call the general function -->
      <xsl:sequence select="gdata:exec-query($service, $query)"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>TODO: Bla bla...</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="query">TODO: Bla bla...</doc:param>
      <doc:param name="since">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:exec-query" as="item()?">
      <!-- the service element -->
      <xsl:param name="service" as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="query"   as="element(gdata:query)"/>
      <!-- xs:date, xs:dateTime or xs:string (an ETag) -->
      <xsl:param name="since"   as="item()?"/>
      <!-- the HTTP request element -->
      <xsl:variable name="req" as="element(http:request)">
         <http:request method="get" href="{ $query }">
            <!-- the GData protocol version, by default "2"  TODO: Require it? -->
            <http:header name="GData-Version" value="{ ($service/@gdata-version, '2.0')[1] }"/>
            <http:header name="Authorization">
               <xsl:attribute name="value">
                  <xsl:choose>
                     <xsl:when test="$service/gdata:client-login">
                         <xsl:text>GoogleLogin auth=</xsl:text>
                         <xsl:value-of select="$service/gdata:client-login"/>
                     </xsl:when>
                     <xsl:when test="$service/gdata:auth-sub">
                         <xsl:text>AuthSub token="</xsl:text>
                         <xsl:value-of select="$service/gdata:auth-sub"/>
                         <xsl:text>"</xsl:text>
                     </xsl:when>
                     <xsl:otherwise>
                        <xsl:sequence select="gdata:error('GDATA009')"/>
                     </xsl:otherwise>
                  </xsl:choose>
               </xsl:attribute>
            </http:header>
            <xsl:sequence select="impl:make-since-header($since)"/>
         </http:request>
      </xsl:variable>
<xsl:message>
   GDATA:EXEC-QUERY REQUEST: <xsl:copy-of select="$req"/>
</xsl:message>
      <!-- send the request and get the response -->
      <xsl:variable name="resp" select="http:send-request($req)"/>
      <!-- does the response look ok? -->
      <xsl:if test="not(count($resp) = (1, 2))">
         <xsl:sequence select="gdata:error('GDATA004')"/>
      </xsl:if>
      <xsl:if test="xs:integer($resp[1]/@status) ne 200">
         <xsl:sequence select="gdata:error('GDATA006', $resp[1]/@status, $resp[1]/@message)"/>
      </xsl:if>
      <!-- if the payload is a document node, we assume this is a feed
           or an entry and return the root element instead -->
      <xsl:sequence select="
          if ( $resp[2] instance of document-node() ) then
            $resp[2]/*
          else
            $resp[2]"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>TODO: Bla bla...  TODO: What about an GDATA:EXEC-QUERY-ALL
         (i.e. with a pre-compiled query, except for the recursing
         parameters, i.e. start-index and max-results)...?  TODO: Why
         is there no $since param here...?</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="query">TODO: Bla bla...</doc:param>
      <doc:param name="start">
         TODO: Bla bla...  TODO: Provide another arity without
         $start (use most of the time, we want really all entries,
         from the very biginning)
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:query-all" as="element(atom:feed)+">
      <!-- the service element -->
      <xsl:param name="service" as="element(gdata:service)"/>
      <!-- the query -->
      <xsl:param name="query"   as="element()+"/>
      <!-- start index -->
      <xsl:param name="start"   as="xs:integer"/>
      <!-- start index as a gdata:param (can not be present in $params, TODO: check) -->
      <xsl:variable name="start-index" as="element()">
         <gdata:start-index>
            <xsl:value-of select="$start"/>
         </gdata:start-index>
      </xsl:variable>
      <!-- one feed -->
      <xsl:variable name="this" select="
          gdata:query($service, ( $query, $start-index ), ())"/>
      <!-- index of the last enry in this feed -->
      <xsl:variable name="last" select="
          xs:integer($this/os:startIndex) + count($this/atom:entry) - 1"/>
      <!-- return this feed -->
      <xsl:sequence select="$this"/>
      <!-- recurse (or not) on following feeds -->
      <xsl:if test="$last lt xs:integer($this/os:totalResults)">
         <xsl:sequence select="
             gdata:query-all($service, $query, $start + count($this/atom:entry))"/>
      </xsl:if>
   </xsl:function>


   <!-- ...........................................................................................
      Query parameters.
      
      - gdata:param-q($value)
      - gdata:param-category($value)
      - gdata:param-category-or($param)
      - gdata:param-category-not($param)
      - gdata:param-author($value)
      - gdata:param-alt($value)
      - gdata:param-updated-min($value)
      - gdata:param-updated-max($value)
      - gdata:param-prettyprint($value)
      - gdata:param-published-min($value)
      - gdata:param-published-max($value)
      - gdata:param-start-index($value)
      - gdata:param-strict($value)
      - gdata:param-max-results($value)
      
      See http://code.google.com/apis/gdata/docs/2.0/reference.html#Queries.
      
      TODO: Document the whole thing.
      
      TODO: Add support for category schemes.  See the above link.
      
      TODO: Add the 'entryID' pseudo-param?
      
      TODO: Add a global param to control using *always* the 'strict'
      parameter (at compile time the better).
   -->

   <doc:doc>
      <doc:desc>Return a raw query parameter 'q'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-q" as="element(gdata:q)">
      <xsl:param name="value" as="xs:string"/>
      <gdata:q>
         <xsl:value-of select="$value"/>
      </gdata:q>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'category'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-category" as="element(gdata:category)">
      <xsl:param name="value" as="xs:string"/>
      <gdata:category>
         <xsl:value-of select="$value"/>
      </gdata:category>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return sub-parameters to apply OR to.  TODO: Adapt
         gdata:compile-query() to take this into account...</doc:desc>
      <doc:param name="param">The sub-parameters.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-category-or" as="element(gdata:category)">
      <xsl:param name="param" as="element()+"/>
      <gdata:category operator="or">
         <xsl:sequence select="$param"/>
      </gdata:category>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return sub-parameters to apply NOT to.  TODO: Adapt
         gdata:compile-query() to take this into account...</doc:desc>
      <doc:param name="params">The sub-parameters.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-category-not" as="element(gdata:category)">
      <xsl:param name="params" as="element()"/>
      <gdata:category operator="not">
         <xsl:sequence select="$params"/>
      </gdata:category>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'author'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-author" as="element(gdata:author)">
      <xsl:param name="value" as="xs:string"/>
      <gdata:author>
         <xsl:value-of select="$value"/>
      </gdata:author>
   </xsl:function>

   <doc:doc>
      <doc:desc>Possible values for the query parameter 'alt'.</doc:desc>
   </doc:doc>
   <xsl:variable name="gdata:param-alt-enum" select="
       'atom',
       'rss',
       'json',
       'json-in-script',
       'atom-in-script',
       'rss-in-script',
       'atom-service'"/>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'alt'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-alt" as="element(gdata:alt)">
      <xsl:param name="value" as="xs:string"/>
      <xsl:if test="not($value = $gdata:param-alt-enum)">
         <xsl:sequence select="gdata:error('GDATA010', $value)"/>
      </xsl:if>
      <gdata:alt>
         <xsl:value-of select="$value"/>
      </gdata:alt>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'updated-min'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.
         TODO: Be a bit more lenient with the $value type.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-updated-min" as="element(gdata:updated-min)">
      <xsl:param name="value" as="xs:dateTime"/>
      <gdata:updated-min>
         <xsl:value-of select="$value"/>
      </gdata:updated-min>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'updated-max'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.
         TODO: Be a bit more lenient with the $value type.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-updated-max" as="element(gdata:updated-max)">
      <xsl:param name="value" as="xs:dateTime"/>
      <gdata:updated-max>
         <xsl:value-of select="$value"/>
      </gdata:updated-max>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'prettyprint'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-prettyprint" as="element(gdata:prettyprint)">
      <xsl:param name="value" as="xs:boolean"/>
      <gdata:prettyprint>
         <xsl:value-of select="$value"/>
      </gdata:prettyprint>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'published-min'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.
         TODO: Be a bit more lenient with the $value type.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-published-min" as="element(gdata:published-min)">
      <xsl:param name="value" as="xs:dateTime"/>
      <gdata:published-min>
         <xsl:value-of select="$value"/>
      </gdata:published-min>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'published-max'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.
         TODO: Be a bit more lenient with the $value type.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-published-max" as="element(gdata:published-max)">
      <xsl:param name="value" as="xs:dateTime"/>
      <gdata:published-max>
         <xsl:value-of select="$value"/>
      </gdata:published-max>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'start-index'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-start-index" as="element(gdata:start-index)">
      <xsl:param name="value" as="xs:integer"/>
      <gdata:start-index>
         <xsl:value-of select="$value"/>
      </gdata:start-index>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'strict'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-strict" as="element(gdata:strict)">
      <xsl:param name="value" as="xs:boolean"/>
      <gdata:strict>
         <xsl:value-of select="$value"/>
      </gdata:strict>
   </xsl:function>

   <doc:doc>
      <doc:desc>Return a raw query parameter 'max-results'.</doc:desc>
      <doc:param name="value">The value to use for that parameter.</doc:param>
   </doc:doc>
   <xsl:function name="gdata:param-max-results" as="element(gdata:max-results)">
      <xsl:param name="value" as="xs:integer"/>
      <gdata:max-results>
         <xsl:value-of select="$value"/>
      </gdata:max-results>
   </xsl:function>


   <!-- ...........................................................................................
      Get feeds and entries.
      
      - gdata:get-entry($service, $entry-uri)
      - gdata:get-entry($service, $entry-uri, $params)
      - gdata:get-entry($service, $entry-uri, $params, $since)
      - gdata:get-feed($service, $feed-uri)
      - gdata:get-feed($service, $feed-uri, $params)
      - gdata:get-feed($service, $feed-uri, $params, $since)
      - gdata:get-all-feeds($service, $feed-uri)
      - gdata:get-all-feeds($service, $feed-uri, $params)
      
      TODO: Old code, adapt to the new query paradigm.  Actually that
      maybe means to delete 'em all (they are *replaced* by the query
      system).  Maybe a few of them could find their way to hign-level
      functions wrapping the query system.
   -->

   <doc:doc>
      <doc:desc>Get an Atom entry from the service, through HTTP.</doc:desc>
      <doc:param name="service">
         The service object to send the request to.
      </doc:param>
      <doc:param name="entry-uri">
         The URI identifying the entry.
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:get-entry" as="element(atom:entry)">
      <!-- the service element -->
      <xsl:param name="service"   as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="entry-uri" as="xs:string"/>
      <!-- delegate -->
      <xsl:sequence select="impl:get-stuff($service, $entry-uri, (), ())"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>Get an Atom entry from the service, through HTTP.</doc:desc>
      <doc:param name="service">
         The service object to send the request to.
      </doc:param>
      <doc:param name="entry-uri">
         The URI identifying the entry.
      </doc:param>
      <doc:param name="params">
         The parameters to use to query the service for the entry.
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:get-entry" as="element(atom:entry)">
      <!-- the service element -->
      <xsl:param name="service"   as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="entry-uri" as="xs:string"/>
      <!-- query parameters -->
      <xsl:param name="params"    as="element(gdata:param)*"/>
      <!-- delegate -->
      <xsl:sequence select="impl:get-stuff($service, $entry-uri, $params, ())"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>Get an Atom entry from the service, through HTTP.</doc:desc>
      <doc:param name="service">
         The service object to send the request to.
      </doc:param>
      <doc:param name="entry-uri">
         The URI identifying the entry.
      </doc:param>
      <doc:param name="params">
         The parameters to use to query the service for the entry.
      </doc:param>
      <doc:param name="since">
         Retrieve the entry only if it was modified since the value of this parameter.
         It can be either an xs:date or an xs:dateTime, or an xs:string (in that later
         case, it is interpreted as an ETag.)
      </doc:param>
   </doc:doc>
   <xsl:function name="gdata:get-entry" as="element(atom:entry)?">
      <!-- the service element -->
      <xsl:param name="service"   as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="entry-uri" as="xs:string"/>
      <!-- query parameters -->
      <xsl:param name="params"    as="element(gdata:param)*"/>
      <!-- get if modified since date or ETag -->
      <xsl:param name="since"     as="item()"/>
      <!-- delegate -->
      <xsl:sequence select="impl:get-stuff($service, $entry-uri, $params, $since)"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>TODO: Bla bla...</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="feed-uri">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:get-feed" as="element(atom:feed)">
      <!-- the service element -->
      <xsl:param name="service"  as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="feed-uri" as="xs:string"/>
      <!-- delegate -->
      <xsl:sequence select="impl:get-stuff($service, $feed-uri, (), ())"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>TODO: Bla bla...</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="feed-uri">TODO: Bla bla...</doc:param>
      <doc:param name="params">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:get-feed" as="element(atom:feed)">
      <!-- the service element -->
      <xsl:param name="service"  as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="feed-uri" as="xs:string"/>
      <!-- query parameters -->
      <xsl:param name="params"   as="element(gdata:param)*"/>
      <!-- delegate -->
      <xsl:sequence select="impl:get-stuff($service, $feed-uri, $params, ())"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>TODO: Bla bla...</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="feed-uri">TODO: Bla bla...</doc:param>
      <doc:param name="params">TODO: Bla bla...</doc:param>
      <doc:param name="since">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:get-feed" as="element(atom:feed)">
      <!-- the service element -->
      <xsl:param name="service"  as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="feed-uri" as="xs:string"/>
      <!-- query parameters -->
      <xsl:param name="params"    as="element(gdata:param)*"/>
      <!-- get if modified since date or ETag -->
      <xsl:param name="since"     as="item()"/>
      <!-- delegate -->
      <xsl:sequence select="impl:get-stuff($service, $feed-uri, $params, $since)"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>TODO: Bla bla...</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="feed-uri">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:get-all-feeds" as="element(atom:feed)+">
      <!-- the service element -->
      <xsl:param name="service"  as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="feed-uri" as="xs:string"/>
      <!-- delegate -->
      <xsl:sequence select="gdata:get-all-feeds($service, $feed-uri, ())"/>
   </xsl:function>

   <doc:doc>
      <doc:desc>TODO: Bla bla...</doc:desc>
      <doc:param name="service">TODO: Bla bla...</doc:param>
      <doc:param name="feed-uri">TODO: Bla bla...</doc:param>
      <doc:param name="params">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:get-all-feeds" as="element(atom:feed)+">
      <!-- the service element -->
      <xsl:param name="service"  as="element(gdata:service)"/>
      <!-- the feed URI -->
      <xsl:param name="feed-uri" as="xs:string"/>
      <!-- query parameters -->
      <xsl:param name="params"   as="element(gdata:param)*"/>
      <!-- start index -->
      <xsl:variable name="start" as="xs:integer" select="
          ( $params[@name eq 'start-index']/@value, 1 )[1]"/>
      <!-- max results (TODO: Default to 25, really?) -->
      <xsl:variable name="max"   as="xs:integer" select="
          ( $params[@name eq 'max-results']/@value,
            $gdata:default-max-results-by-chunk )[1]"/>
      <xsl:variable name="new-params" as="element(gdata:param)+">
         <xsl:sequence select="$params[not(@name eq 'start-index')]"/>
         <xsl:if test="empty($params[@name eq 'max-results'])">
            <gdata:param name="max-results" value="{ $max }"/>
         </xsl:if>
      </xsl:variable>
      <xsl:sequence select="
          impl:get-all-feeds($service, $feed-uri, $start, $max, $new-params)"/>
   </xsl:function>


   <!-- ...........................................................................................
      Create new entries.
      
      TODO: Create functions to create entries with GData.  But
      first, the GET functions...  Then base on those...
   -->

   <!-- ... -->


   <!-- ...........................................................................................
      Error handling.
      
      - $gdata:errors
      - gdata:error($code)
      - gdata:error($code, $arg-1)
      - gdata:error($code, $arg-1, $arg-2)
      - impl:error($code, $args)
      
      TODO: Must be in impl:*.
   -->

   <doc:doc>
      <doc:desc>Error codes namespace URI.</doc:desc>
   </doc:doc>
   <xsl:variable name="gdata:error-namespace" as="xs:string" select="
       'http://fgeorges.org/ns/goole/data/errors'"/>

   <doc:doc>
      <doc:desc>Error codes and messages.</doc:desc>
   </doc:doc>
   <xsl:variable name="gdata:errors" as="element(e)+">
      <e code="GDATA001">Authentication: wrong HTTP response.</e>
      <e code="GDATA002">Authentication: return is not text but '<arg pos="1"/>'.</e>
      <e code="GDATA003">Authentication: HTTP server returned error '<arg pos="1"/> - <arg pos="2"/>'.</e>
      <e code="GDATA004">Get atom element: wrong HTTP response.</e>
      <e code="GDATA005">Get atom element: return is not XML but '<arg pos="1"/>'.</e>
      <e code="GDATA006">Get atom element: HTTP server returned error '<arg pos="1"/> - <arg pos="2"/>'.</e>
      <e code="GDATA007">Not implemented yet.</e>
      <e code="GDATA008">gdata:get-entry-since: wrong entry selector type.</e>
      <e code="GDATA009">Service is not authenticated.</e>
      <e code="GDATA010">The value of the query parameter 'alt' in not correct: '<arg pos="1"/>'.</e>
      <e code="GDATA011">Unknown category operator: '<arg pos="1"/>'.</e>
      <!-- TODO: Specific to gdocs.xsl.  Implement a way to allow
           other modules to define their own message tables... -->
      <e code="GDATA012">The value of the query parameter 'exportFormat' in not correct: '<arg pos="1"/>'.</e>
   </xsl:variable>

   <doc:doc>
      <doc:desc>Front-end error function, without argument to the message.</doc:desc>
      <doc:param name="code">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:error">
      <xsl:param name="code" as="xs:string"/>
      <xsl:call-template name="impl:error">
         <xsl:with-param name="code" select="$code"/>
      </xsl:call-template>
   </xsl:function>

   <doc:doc>
      <doc:desc>Front-end error function, with 1 argument to the message.</doc:desc>
      <doc:param name="code">TODO: Bla bla...</doc:param>
      <doc:param name="arg-1">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:error">
      <xsl:param name="code" as="xs:string"/>
      <xsl:param name="arg-1" as="xs:string"/>
      <xsl:call-template name="impl:error">
         <xsl:with-param name="code" select="$code"/>
         <xsl:with-param name="args" select="$arg-1"/>
      </xsl:call-template>
   </xsl:function>

   <doc:doc>
      <doc:desc>Front-end error function, with 2 arguments to the message.</doc:desc>
      <doc:param name="code">TODO: Bla bla...</doc:param>
      <doc:param name="arg-1">TODO: Bla bla...</doc:param>
      <doc:param name="arg-2">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:function name="gdata:error">
      <xsl:param name="code" as="xs:string"/>
      <xsl:param name="arg-1" as="xs:string"/>
      <xsl:param name="arg-2" as="xs:string"/>
      <xsl:call-template name="impl:error">
         <xsl:with-param name="code" select="$code"/>
         <xsl:with-param name="args" select="$arg-1, $arg-2"/>
      </xsl:call-template>
   </xsl:function>

   <doc:doc>
      <doc:desc>Shared implementation for all front-end error functions.</doc:desc>
      <doc:param name="code">TODO: Bla bla...</doc:param>
      <doc:param name="args">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:template name="impl:error">
      <xsl:param name="code" as="xs:string"/>
      <xsl:param name="args" as="xs:string*"/>
      <xsl:variable name="err-msg" as="xs:string+">
         <xsl:variable name="e" as="element(e)" select="$gdata:errors[@code eq $code]"/>
         <xsl:apply-templates select="$e/node()" mode="impl:error">
            <xsl:with-param name="args" select="$args" tunnel="yes"/>
         </xsl:apply-templates>
      </xsl:variable>
      <xsl:sequence select="
          error(
            QName($gdata:error-namespace, concat('err:', $code)),
            string-join($err-msg, ''))"/>
   </xsl:template>

   <doc:doc>
      <doc:desc>Replace formal arguments with their actual values in error messages.</doc:desc>
      <doc:param name="args">TODO: Bla bla...</doc:param>
   </doc:doc>
   <xsl:template match="arg" mode="impl:error" as="xs:string">
      <xsl:param name="args" as="xs:string+" tunnel="yes"/>
      <xsl:sequence select="$args[xs:integer(current()/@pos)]"/>
   </xsl:template>

   <doc:doc>
      <doc:desc>Text is inserted itself in error messages.</doc:desc>
   </doc:doc>
   <xsl:template match="text()" mode="impl:error" as="xs:string">
      <xsl:sequence select="."/>
   </xsl:template>

   <doc:doc>
      <doc:desc>Everything else is an error in error message.  We use
         directly error() to prevent any cycle.</doc:desc>
   </doc:doc>
   <xsl:template match="node()" mode="impl:error" priority="-1">
      <xsl:sequence select="
          error(
            QName($gdata:error-namespace, 'err:GDATA000'),
            concat('Error in the error system, unknown node: ', name(.)))"/>
   </xsl:template>


   <!-- ...........................................................................................
      Copyright.
   -->

   <doc:doc scope="stylesheet">
      <doc:p>Copyright.</doc:p>
      <doc:pre>
Copyright (c) 2009 Florent Georges.

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS COMMENT.

The contents of this file are subject to the Mozilla Public License
Version 1.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/.

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
the License for the specific language governing rights and limitations
under the License.

The Original Code is: all this file.

The Initial Developer of the Original Code is Florent Georges.

Contributor(s): none.
      </doc:pre>
   </doc:doc>

</xsl:stylesheet>
